<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <title>缩放</title>
  <style>
    html {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
    }

    body {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
    }

    #canvas {
      display: block;
    }
  </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="../lib/pixi-6.0.4.min.js"></script>
<script src="../lib/stats-r17.min.js"></script>
<script>
  (function () {
    window.PIXI = PIXI
    PIXI.utils.skipHello()
    const stats = new Stats()
    stats.showPanel(0) // 0: fps, 1: ms, 2: mb, 3+: custom
    stats.dom.style.transformOrigin = '0 0'
    stats.dom.style.transform = 'translate(0,100px) scale(1.2)'
    document.body.appendChild(stats.dom)

    let pageWidth = 0
    let pageHeight = 0
    let canvasWidth = 0
    let canvasHeight = 0
    let renderer = null

    function onResize (e) {
      pageWidth = window.innerWidth
      pageHeight = window.innerHeight
      canvasWidth = pageWidth
      canvasHeight = pageHeight
      // canvasWidth = 500
      // canvasHeight = 500
      renderer && renderer.resize(canvasWidth, canvasHeight)
    }

    onResize()

    const $canvas = document.querySelector('#canvas')
    renderer = new PIXI.Renderer({
      view: $canvas,
      width: canvasWidth,
      height: canvasHeight,
      resolution: window.devicePixelRatio, // 放大 devicePixelRatio 倍
      autoDensity: true, // 缩小 devicePixelRatio 倍
      backgroundAlpha: 1,
      backgroundColor: 0xeef2f8,
      antialias: true
    })
    const stage = new PIXI.Container()
    stage.name = 'stage'

    const ticker = new PIXI.Ticker()

    let mouse = { x: 0, y: 0 }
    let moving = false
    let mouseDown = false
    let scaling = false
    let lastScale = 1
    let scale = 1
    let translateX = 0
    let translateY = 0

    let targetScale = 1
    let targetTranslateX = 0
    let targetTranslateY = 0

    class Gay extends PIXI.Sprite {
      constructor () {
        super()
        this.name = 'Gay'
        this.texture = PIXI.Texture.from('./gay.jpg')
        this.anchor.set(0.5)
        this._startX = canvasWidth * 0.5
        this._startY = canvasHeight * 0.5
        this.dirty = false
        this.position.set(this._startX, this._startY)
      }

      setTranslate () {
        this.position.set(this._startX + translateX, this._startY + translateY)
      }

      setScale () {
        this.scale.set(scale, scale)
        this.position.x = mouse.x + (this.position.x - mouse.x) / lastScale * scale
        this.position.y = mouse.y + (this.position.y - mouse.y) / lastScale * scale
        this._startX = this.position.x - translateX
        this._startY = this.position.y - translateY
      }
    }

    class GridBackground extends PIXI.Graphics {
      constructor () {
        super()
        this.name = 'GridBackground'
        this.dirty = false
        this.gridSize = 50
        this._startX = 0
        this._startY = 0
        this.translateX = 0
        this.translateY = 0
        const gridVector = new PIXI.DisplayObject()
        gridVector.name = 'gridVector'
        this.gridVector = gridVector
        this.setTranslate()
      }

      setVectorTranslate () {
        this.gridVector.position.set(this._startX + translateX, this._startY + translateY)
      }

      setTranslate () {
        this.setVectorTranslate()
        const gridSize = this.gridSize
        this.clear()
        this.lineStyle(1, 0xdee0e3)
        const gridVector = this.gridVector
        const newTranslateX = gridVector.position.x % gridSize
        const newTranslateY = gridVector.position.y % gridSize
        // (i-1) 是为了多画一条线
        // 竖向线
        for (let i = 0; (i - 1) * gridSize <= canvasWidth; i++) {
          this.moveTo(newTranslateX + i * gridSize, 0)
          this.lineTo(newTranslateX + i * gridSize, canvasHeight)
        }
        // 横向线
        for (let i = 0; (i - 1) * gridSize <= canvasHeight; i++) {
          this.moveTo(0, newTranslateY + i * gridSize)
          this.lineTo(canvasWidth, newTranslateY + i * gridSize)
        }
      }

      setVectorScale () {
        const gridVector = this.gridVector
        gridVector.scale.set(scale, scale)
        gridVector.position.x = mouse.x + (gridVector.position.x - mouse.x) / lastScale * scale
        gridVector.position.y = mouse.y + (gridVector.position.y - mouse.y) / lastScale * scale
        this._startX = gridVector.position.x - translateX
        this._startY = gridVector.position.y - translateY
      }

      setScale () {
        this.setVectorScale()
        const gridVector = this.gridVector
        this.gridSize = 50 * scale
        this.setTranslate()
      }
    }

    const grid = new GridBackground()
    stage.addChild(grid)

    const gay = new Gay()
    stage.addChild(gay)

    stage.interactive = true

    let startPos = { x: 0, y: 0 }
    let startMouse = { x: 0, y: 0 }
    $canvas.addEventListener('mousedown', function (e) {
      mouse = {
        x: e.pageX,
        y: e.pageY
      }
      mouseDown = true
      moving = true
      // 终止缩放
      scaling = false
      targetScale = scale

      targetTranslateX = translateX
      targetTranslateY = translateY
      startMouse = {
        x: mouse.x,
        y: mouse.y
      }
      startPos = {
        x: translateX,
        y: translateY
      }
    })
    $canvas.addEventListener('mousemove', function (e) {
      if (mouseDown) {
        mouse = {
          x: e.pageX,
          y: e.pageY
        }
        targetTranslateX = startPos.x + (mouse.x - startMouse.x)
        targetTranslateY = startPos.y + (mouse.y - startMouse.y)
      }
    })
    $canvas.addEventListener('mouseup', function (e) {
      mouseDown = false
    })
    $canvas.onwheel = function (e) {
      mouse = {
        x: e.pageX,
        y: e.pageY
      }
      scaling = true
      // 终止移动
      moving = false
      targetTranslateX = translateX
      targetTranslateY = translateY

      const delta = e.wheelDelta ? e.wheelDelta : -e.deltaY
      targetScale = delta > 0 ? scale * 1.4 : scale / 1.4
    }

    const loop = () => {
      stats.begin()
      if (moving) {
        const deltaX = (targetTranslateX - translateX) * 0.1
        const deltaY = (targetTranslateY - translateY) * 0.1
        // 差值小于0.001并且鼠标抬起，标为移动结束
        if (Math.abs(deltaX) <= 0.001 && Math.abs(deltaY) <= 0.001 && !mouseDown) {
          moving = false
        }
        else {
          translateX += deltaX
          translateY += deltaY
        }
        grid.setTranslate()
        gay.setTranslate()
      }
      if (scaling) {
        lastScale = scale
        let deltaScale = (targetScale - scale) * 0.1
        // 差值小于0.001，标为缩放结束
        if (Math.abs(deltaScale) <= 0.001) {
          scaling = false
        }
        else {
          scale += deltaScale
        }
        grid.setScale()
        gay.setScale()
      }
      renderer.render(stage)
      stats.end()
    }

    ticker.add(loop)
    ticker.start()

    renderer.render(stage)

    // 供pixi的inspector使用
    globalThis.__PIXI_STAGE__ = stage
    globalThis.__PIXI_RENDERER__ = renderer

    window.onresize = onResize
  })()
</script>
</body>
</html>